/*
 * Copyright (c) 2016. 速位中国
 */

package com.grpc_server.grpc_netty;
/*

                  写字楼里写字间，写字间里程序员；
                  程序人'猿'写程序，又拿程序换酒钱。
                  酒醒只在网上坐，酒醉还来网下眠；
                  酒醉酒醒日复日，网上网下年复年。
                  但愿老死电脑间，不愿鞠躬老板前；
                  奔驰宝马贵者趣，公交自行程序员。
                  别人笑我忒疯癫，我笑自己命太贱；
                  不见满街漂亮妹，哪个归得程序'猿'？
━━━━━━神兽出没━━━━━━
 ┏┓   ┏┓
┏┛┻━━━┛┻┓
┃       ┃
┃   ━   ┃
┃ ┳┛ ┗┳ ┃
┃       ┃
┃   ┻   ┃
┃       ┃
┗━┓   ┏━┛
  ┃   ┃                    神兽保佑, 永无BUG!
  ┃   ┃       Code is far away from bug with the animal protecting
  ┃   ┗━━━┓
  ┃       ┣┓
  ┃       ┏┛
  ┗┓┓┏━┳┓┏┛
   ┃┫┫ ┃┫┫
   ┗┻┛ ┗┻┛
 */

import com.grpc_server.utils.FileUtil;
import io.grpc.Server;
import io.grpc.netty.NettyServerBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.File;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by 石头哥哥 on 1/12/16.
 * <p>
 * grpc
 * <p>
 * server.net
 * <p>
 * comments:  网络服务
 */
@Component
public class GrpcNetService {

    private final Logger logger = LoggerFactory.getLogger(GrpcNetService.class);


    /**
     * server
     */
    private Server server;

    /**
     * server bind port
     */
    private int port;

    @Resource
    private NettyServerBuilder nettyServerBuilder;


    /**
     * handle logic  thread pool
     */
    private static final ExecutorService executorService =
            Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()
                    , new NamedThreadFactory("logic-handler"));


    /**
     * 设置 builder相关属性  具体详见 nettyServerBuilder 对象
     */
    private void initServerBuilder() {

        //init server builder
        nettyServerBuilder.executor(executorService);


        //loading config
        Properties properties = FileUtil.buildProperties("res/grpc.properties");
        if (properties != null) {
            String certChain = properties.getProperty("certChain");
            File gem_file = null, privateKey_file = null;
            if (!certChain.equals("")) {
                gem_file = new File(certChain);
            }
            String privateKey = properties.getProperty("privateKey");
            if (!privateKey.equals("")) {
                privateKey_file = new File(privateKey);
            }
            //security transport
            if (gem_file != null && privateKey_file != null) {
                nettyServerBuilder.useTransportSecurity(gem_file, privateKey_file);
            }

            //service port
            port = Integer.parseInt(properties.getProperty("grpc_port"));

            // TODO: 1/13/16  loading more config data and init  nettyServerBuilder

            //clear ref
            properties.clear();

        } else {
            throw new RuntimeException("not found 'res/grpc.properties' file, please check out! ");
        }

    }

    /**
     * 启动服务
     *
     * @throws Exception
     */
    public void startGrpc() throws Exception {

        //init builder
        initServerBuilder();

        //bind service
        server = nettyServerBuilder.build().start();

        //register hook
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                // Use stderr here since the logger may has been reset by its JVM shutdown hook.
                logger.debug("*** shutting down gRPC server since JVM is shutting down");
                if (server != null) {
                    server.shutdown();// shut down
                }
                executorService.shutdown();
                logger.debug("*** server shut down");
            }
        });

        if (logger.isDebugEnabled()) {
            logger.debug("grpc_server bind service port:" + port);
        }

    }


    /**
     * Await termination on the main thread since the grpc library uses daemon threads.
     *
     * @throws InterruptedException
     */
    public void awaitTermination() throws InterruptedException {
        server.awaitTermination();
    }


    /**
     * 线程池工厂
     */
    static class NamedThreadFactory implements ThreadFactory {

        private int _prio;
        private String _name;
        private AtomicInteger _threadNumber = new AtomicInteger(1);
        private ThreadGroup _group;

        /**
         * @return
         */
        public String get_name() {
            return _name;
        }

        /**
         * @param name 线程池名
         */
        public NamedThreadFactory(String name) {
            _prio = Thread.NORM_PRIORITY;//线程默认优先级
            _name = name;
            _group = new ThreadGroup(_name);
        }


        /**
         * @param name 线程池名
         */
        public NamedThreadFactory(String name, int _prio) {
            _prio = _prio;
            _name = name;
            _group = new ThreadGroup(_name);
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(_group, r);
            t.setName(_name + "-" + "#-" + _threadNumber.getAndIncrement());
            t.setPriority(_prio);
            return t;
        }

        public ThreadGroup getGroup() {
            return _group;
        }
    }


}
